/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.ssau.entities;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.FinderException;
import javax.ejb.NoSuchEntityException;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.RemoveException;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.jdom.Element;
import ru.ssau.lib.StringIntegerPair;
import ru.ssau.utils.XMLHelper;
import ru.ssau.utils.DatabaseUtil;
import ru.ssau.utils.EntityUtil;


public class UserBean implements EntityBean {
    private static final String REQUEST_USER_ADD =
            "INSERT INTO Users(UserID,UserName,RegDate,UserInfo) VALUES(?,?,?,?)";
    private static final String REQUEST_USER_ID_SEQUENCE_GET_NEXT_VALUE =
            "SELECT UserIDSeq.NEXTVAL FROM dual";
    private static final String REQUEST_USER_DELETE_BY_ID =
            "DELETE FROM Users WHERE UserID=?";
    private static final String REQUEST_USER_GET_BY_ID =
            "SELECT UserName,RegDate,UserInfo FROM Users WHERE UserId=?";
    private static final String REQUEST_USER_UPDATE =
            "UPDATE Users SET UserName=?, RegDate=?, UserInfo=? WHERE UserId=?";
    private static final String REQUEST_USERS_GET_ALL =
            "SELECT UserId,UserName,RegDate,UserInfo FROM Users";
    private static final String REQUEST_USERS_FIND_BY_NAME =
            "SELECT UserId FROM Users WHERE UPPER(UserName) LIKE UPPER(?)";
    private static final String REQUEST_USER_FIND_BY_ID =
            "SELECT UserId FROM Users WHERE UserId = ?";
    private static final String REQUEST_USERS_GET_ALL_AS_STRING_INTEGER_PAIRS =
            "SELECT UserId, UserName FROM Users";

    private Integer id;
    private String name;
    private Date registrationDate;
    private String info;
    private AuthorizationInfoLocal authorizationInfo;

    private EntityContext entityContext;
    private DataSource dataSource;

    private boolean isEdited;

    public void setEntityContext(EntityContext ctx) {
        try {
            entityContext = ctx;
            dataSource = DatabaseUtil.getDataSource();
            isEdited = false;
        } catch (NamingException ex) {
            throw new EJBException("Cannon lookup connection pool on server", ex);
        }
    }

    public void unsetEntityContext() {
        entityContext = null;
    }

    public Integer ejbCreate(String name, Date registrationDate, String info) throws CreateException {
        PreparedStatement idGeneratePS = null;
        PreparedStatement updatePS = null;
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            //id generation
            idGeneratePS = conn.prepareStatement(REQUEST_USER_ID_SEQUENCE_GET_NEXT_VALUE);
            ResultSet rs = idGeneratePS.executeQuery();
            if(rs.next()) {
                id = rs.getInt(1);
            } else {
                throw new CreateException("Cannot generate id for user");
            }
            //database updating
            updatePS = conn.prepareStatement(REQUEST_USER_ADD);
            updatePS.setInt(1, id.intValue());
            updatePS.setString(2, name);
            Timestamp regTime = new Timestamp(registrationDate.getTime());
            updatePS.setTimestamp(3, regTime);
            updatePS.setString(4, info);
            if(updatePS.executeUpdate() != 1) {
                throw new CreateException("Insertion error");
            }
            this.name = name;
            this.registrationDate = registrationDate;
            this.info = info;
            isEdited = false;
            return id;
        } catch(SQLException ex) {
            throw new EJBException("Error in INSERT statement", ex);
        } finally {
            DatabaseUtil.closeConnection(conn, idGeneratePS);
            DatabaseUtil.closeConnection(null, updatePS);
        }
    }

    public void ejbPostCreate(String name, Date registrationDate, String info) throws CreateException {

    }

    public Integer ejbFindByPrimaryKey(Integer primaryKey) throws ObjectNotFoundException {
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            ps = conn.prepareStatement(REQUEST_USER_FIND_BY_ID);
            ps.setInt(1, primaryKey.intValue());
            ResultSet rs = ps.executeQuery();
            if(!rs.next()) {
                throw new ObjectNotFoundException("No entity found by id = " + primaryKey.toString());
            }
            return primaryKey;
        } catch(SQLException ex) {
            throw new EJBException("Error in SELECT statement", ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }

    public Collection<Integer> ejbFindByName(String caption) {
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            LinkedList<Integer> keys = new LinkedList<Integer>();
            ps = conn.prepareStatement(REQUEST_USERS_FIND_BY_NAME);
            ps.setString(1, DatabaseUtil.getContainsPattern(caption));
            ResultSet rs = ps.executeQuery();
            while(rs.next()) {
                Integer key = Integer.valueOf(rs.getInt(1));
                keys.add(key);
            }
            return keys;
        } catch(SQLException ex) {
            throw new EJBException("Error in SELECT statement", ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }

    public void ejbActivate() {

    }

    public void ejbPassivate() {

    }

    public void ejbLoad() {
        PreparedStatement ps = null;
        Connection conn = null;
        id = (Integer) entityContext.getPrimaryKey();
        try {
            conn = dataSource.getConnection();
            ps = conn.prepareStatement(REQUEST_USER_GET_BY_ID);
            ps.setInt(1, id);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                name = rs.getString("UserName");
                registrationDate = new Date(rs.getTimestamp("regDate").getTime());
                info = rs.getString("UserInfo");
                AuthorizationInfoLocalHome authorizationInfoHome = EntityUtil.getAuthorizationInfoLocalHome();
                //Кусочек быдлокода)
                try {
                    authorizationInfo = authorizationInfoHome.findByUserId(id);
                } catch(FinderException ex) { }
            } else {
                throw new NoSuchEntityException("No entity found by id = " + id.toString());
            }
            isEdited = false;
        } catch (SQLException ex) {
            throw new EJBException("Error in SELECT statement", ex);
        } catch(NamingException ex) {
            throw new EJBException("Cannot find AuthorizationInfo home object", ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }

    public void ejbStore() {
        if(!isEdited) return;
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            ps = conn.prepareStatement(REQUEST_USER_UPDATE);
            ps.setString(1, name);
            Timestamp regTime = new Timestamp(registrationDate.getTime());
            ps.setTimestamp(2, regTime);
            ps.setString(3, info);
            ps.setInt(4, id.intValue());
            if(ps.executeUpdate() < 1) {
                throw new NoSuchEntityException("No entity found by id = " + id.toString());
            }
            isEdited = false;
        } catch(SQLException ex) {
            throw new EJBException("Error in UPDATE statement", ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }

    public void ejbRemove() throws RemoveException {
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            ps = conn.prepareStatement(REQUEST_USER_DELETE_BY_ID);
            ps.setInt(1, id.intValue());
            if(ps.executeUpdate() < 1) {
                throw new NoSuchEntityException("No entity found by id = " + id.toString());
            }
            isEdited = false;
        } catch(SQLException ex) {
            throw new EJBException("Error in DELETE statement", ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }
    
    public Collection<StringIntegerPair> ejbHomeGetAllAsStringIntegerPairs() {
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            ps = conn.prepareStatement(REQUEST_USERS_GET_ALL_AS_STRING_INTEGER_PAIRS);
            ResultSet rs = ps.executeQuery();
            Collection<StringIntegerPair> pairs = new LinkedList<StringIntegerPair>();
            while(rs.next()) {
                Integer messageId = Integer.valueOf(rs.getInt("UserId"));
                String messageCaption = rs.getString("UserName");
                StringIntegerPair pair = new StringIntegerPair(messageId, messageCaption);
                pairs.add(pair);
            }
            return pairs;
        } catch(SQLException ex) {
            throw new EJBException("Error in SELECT statement", ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        isEdited = true;
    }

    public Date getRegistrationDate() {
        return registrationDate;
    }

    public void setRegistrationDate(Date registrationDate) {
        this.registrationDate = registrationDate;
        isEdited = true;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
        isEdited = true;
    }

    public AuthorizationInfoLocal getAuthorizationInfo() {
        return authorizationInfo;
    }

    public void setAuthorizationInfo(AuthorizationInfoLocal authorizationInfo) {
        this.authorizationInfo = authorizationInfo;
        isEdited = true;
    }

    public String getStringRepresentation() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getName());
        sb.append(": [id=");
        sb.append(id);
        sb.append(", name=");
        sb.append(name);
        sb.append(", regDate=[");
        sb.append(registrationDate.toString());
        sb.append("], info=");
        sb.append(info);
        sb.append(", authInfo=[");
        sb.append(authorizationInfo.getStringRepresentation());
        sb.append("]]");
        return sb.toString();
    }

    public Element toXMLElement() {
        Element root = new Element("User");
        XMLHelper.addElement(root, "id", id);
        XMLHelper.addElement(root, "name", name);
        XMLHelper.addElement(root, "registrationDate", registrationDate);
        XMLHelper.addElement(root, "info", info);
        XMLHelper.addElement(root, authorizationInfo.toXMLElement());
        return root;
    }

    public void loadFromXMLElement(Element data) throws NumberFormatException {
        throw new UnsupportedOperationException();
    }

}
